from pyjvm.classfile.reader import ClassBytesReader
from pyjvm.runtime.metadata import Class
from pyjvm.runtime.thread import Frame, Thread


def branch(frame: Frame, offset: int):
    pc = frame.thread.pc
    next_pc = pc + offset
    frame.set_next_pc(next_pc)


class BytecodeReader(ClassBytesReader):

    def __init__(self, code: bytes, pc: int = 0):
        ClassBytesReader.__init__(self, code, pc)

    def skip_padding(self):
        while self.pc() % 4:
            self.u1()


class Instruction:

    def __init__(self):
        self.record = None

    def fetch_operands(self, reader: BytecodeReader):
        pass

    def execute(self, frame: Frame):
        pass


class BranchInstruction(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.offset = None

    def fetch_operands(self, reader: BytecodeReader):
        self.offset = reader.int16()


class Index8Instruction(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.index = None

    def fetch_operands(self, reader: BytecodeReader):
        self.index = reader.uint8()


class Index16Instruction(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.index = None

    def fetch_operands(self, reader: BytecodeReader):
        self.index = reader.int16()
        # ub1 = reader.read_u1_int()
        # ub2 = reader.read_u1_int()
        # self.index = ((ub1 << 8) | ub2)


class NOP(Instruction):
    """
    空指令
    """

    def execute(self, frame: Frame):
        pass


class ClassInitializer:

    @classmethod
    def init_class(cls, thread: Thread, klass: Class):
        klass.inited = True
        ClassInitializer.__invoke_init_method(thread, klass)
        ClassInitializer.__init_super_class(thread, klass)

    @classmethod
    def __invoke_init_method(cls, thread: Thread, klass: Class):
        init_method = klass.get_init_method()
        if init_method is None:
            return
        new_frame = thread.new_frame(init_method)
        thread.push_frame(new_frame)

    @classmethod
    def __init_super_class(cls, thread: Thread, klass: Class):
        if klass.is_interface:
            return
        super_class: Class = klass.super_class
        if super_class is not None and not super_class.inited:
            ClassInitializer.init_class(thread, super_class)


__all__ = ['branch', 'ClassInitializer', 'Instruction', 'Index8Instruction', 'Index16Instruction',
           'BranchInstruction', 'BytecodeReader', 'NOP']
